home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / MAME / src / png.c < prev    next >
C/C++ Source or Header  |  2000-05-04  |  16KB  |  689 lines

  1. /*********************************************************************
  2.  
  3.   png.c
  4.  
  5.   PNG reading functions.
  6.  
  7.   07/15/1998 Created by Mathis Rosenhauer
  8.   10/02/1998 Code clean up and abstraction by Mike Balfour
  9.              and Mathis Rosenhauer
  10.   10/15/1998 Image filtering. MLR
  11.   11/09/1998 Bit depths 1-8 MLR
  12.   11/10/1998 Some additional PNG chunks recognized MLR
  13.   05/14/1999 Color type 2 and PNG save functions added
  14.   05/15/1999 Handle RGB555 while saving, use osd_fxxx
  15.              functions for writing MSH
  16.  
  17.   TODO : Fully comply with the "Recommendations for Decoders"
  18.          of the W3C
  19.  
  20. *********************************************************************/
  21.  
  22. #include <math.h>
  23. #include <zlib.h>
  24. #include "driver.h"
  25. #include "png.h"
  26.  
  27. extern char build_version[];
  28.  
  29. /* convert_uint is here so we don't have to deal with byte-ordering issues */
  30. static UINT32 convert_from_network_order (UINT8 *v)
  31. {
  32.     UINT32 i;
  33.  
  34.     i = (v[0]<<24) | (v[1]<<16) | (v[2]<<8) | (v[3]);
  35.     return i;
  36. }
  37.  
  38. int png_unfilter(struct png_info *p)
  39. {
  40.     int i, j, bpp, filter;
  41.     INT32 prediction, pA, pB, pC, dA, dB, dC;
  42.     UINT8 *src, *dst;
  43.  
  44.     if((p->image = (UINT8 *)malloc (p->height*p->rowbytes))==NULL)
  45.     {
  46.         logerror("Out of memory\n");
  47.         free (p->fimage);
  48.         return 0;
  49.     }
  50.  
  51.     src = p->fimage;
  52.     dst = p->image;
  53.     bpp = p->bpp;
  54.  
  55.     for (i=0; i<p->height; i++)
  56.     {
  57.         filter = *src++;
  58.         if (!filter)
  59.         {
  60.             memcpy (dst, src, p->rowbytes);
  61.             src += p->rowbytes;
  62.             dst += p->rowbytes;
  63.         }
  64.         else
  65.             for (j=0; j<p->rowbytes; j++)
  66.             {
  67.                 pA = (j<bpp) ? 0: *(dst - bpp);
  68.                 pB = (i<1) ? 0: *(dst - p->rowbytes);
  69.                 pC = ((j<bpp)||(i<1)) ? 0: *(dst - p->rowbytes - bpp);
  70.  
  71.                 switch (filter)
  72.                 {
  73.                 case PNG_PF_Sub:
  74.                     prediction = pA;
  75.                     break;
  76.                 case PNG_PF_Up:
  77.                     prediction = pB;
  78.                     break;
  79.                 case PNG_PF_Average:
  80.                     prediction = ((pA + pB) / 2);
  81.                     break;
  82.                 case PNG_PF_Paeth:
  83.                     prediction = pA + pB - pC;
  84.                     dA = abs(prediction - pA);
  85.                     dB = abs(prediction - pB);
  86.                     dC = abs(prediction - pC);
  87.                     if (dA <= dB && dA <= dC) prediction = pA;
  88.                     else if (dB <= dC) prediction = pB;
  89.                     else prediction = pC;
  90.                     break;
  91.                 default:
  92.                     logerror("Unknown filter type %i\n",filter);
  93.                     prediction = 0;
  94.                 }
  95.                 *dst = 0xff & (*src + prediction);
  96.                 dst++; src++;
  97.             }
  98.     }
  99.  
  100.     free (p->fimage);
  101.     return 1;
  102. }
  103.  
  104. int png_verify_signature (void *fp)
  105. {
  106.     INT8 signature[8];
  107.  
  108.     if (osd_fread (fp, signature, 8) != 8)
  109.     {
  110.         logerror("Unable to read PNG signature (EOF)\n");
  111.         return 0;
  112.     }
  113.  
  114.     if (memcmp(signature, PNG_Signature, 8))
  115.     {
  116.         logerror("PNG signature mismatch found: %s expected: %s\n",signature,PNG_Signature);
  117.         return 0;
  118.     }
  119.     return 1;
  120. }
  121.  
  122. int png_inflate_image (struct png_info *p)
  123. {
  124.     unsigned long fbuff_size;
  125.  
  126.     fbuff_size = p->height * (p->rowbytes + 1);
  127.  
  128.     if((p->fimage = (UINT8 *)malloc (fbuff_size))==NULL)
  129.     {
  130.         logerror("Out of memory\n");
  131.         free (p->zimage);
  132.         return 0;
  133.     }
  134.  
  135.     if (uncompress(p->fimage, &fbuff_size, p->zimage, p->zlength) != Z_OK)
  136.     {
  137.         logerror("Error while inflating image\n");
  138.         return 0;
  139.     }
  140.  
  141.     free (p->zimage);
  142.     return 1;
  143. }
  144.  
  145. int png_read_file(void *fp, struct png_info *p)
  146. {
  147.     /* translates color_type to bytes per pixel */
  148.     const int samples[] = {1, 0, 3, 1, 2, 0, 4};
  149.  
  150.     UINT32 chunk_length, chunk_type=0, chunk_crc, crc;
  151.     UINT8 *chunk_data, *temp;
  152.     UINT8 str_chunk_type[5], v[4];
  153.  
  154.     struct idat
  155.     {
  156.         struct idat *next;
  157.         int length;
  158.         UINT8 *data;
  159.     } *ihead, *pidat;
  160.  
  161.     if ((ihead = malloc (sizeof(struct idat))) == 0)
  162.         return 0;
  163.  
  164.     pidat = ihead;
  165.  
  166.     p->zlength = 0;
  167.     p->num_palette = 0;
  168.     p->num_trans = 0;
  169.     p->trans = NULL;
  170.     p->palette = NULL;
  171.  
  172.     if (png_verify_signature(fp)==0)
  173.         return 0;
  174.  
  175.     while (chunk_type != PNG_CN_IEND)
  176.     {
  177.         if (osd_fread(fp, v, 4) != 4)
  178.             logerror("Unexpected EOF in PNG\n");
  179.         chunk_length=convert_from_network_order(v);
  180.  
  181.         if (osd_fread(fp, str_chunk_type, 4) != 4)
  182.             logerror("Unexpected EOF in PNG file\n");
  183.  
  184.         str_chunk_type[4]=0; /* terminate string */
  185.  
  186.         crc=crc32(0,str_chunk_type, 4);
  187.         chunk_type = convert_from_network_order(str_chunk_type);
  188.  
  189.         if (chunk_length)
  190.         {
  191.             if ((chunk_data = (UINT8 *)malloc(chunk_length+1))==NULL)
  192.             {
  193.                 logerror("Out of memory\n");
  194.                 return 0;
  195.             }
  196.             if (osd_fread (fp, chunk_data, chunk_length) != chunk_length)
  197.             {
  198.                 logerror("Unexpected EOF in PNG file\n");
  199.                 free(chunk_data);
  200.                 return 0;
  201.             }
  202.  
  203.             crc=crc32(crc,chunk_data, chunk_length);
  204.         }
  205.         else
  206.             chunk_data = NULL;
  207.  
  208.         if (osd_fread(fp, v, 4) != 4)
  209.             logerror("Unexpected EOF in PNG\n");
  210.         chunk_crc=convert_from_network_order(v);
  211.  
  212.         if (crc != chunk_crc)
  213.         {
  214.             logerror("CRC check failed while reading PNG chunk %s\n",str_chunk_type);
  215.             logerror("Found: %08X  Expected: %08X\n",crc,chunk_crc);
  216.             return 0;
  217.         }
  218.  
  219.         logerror("Reading PNG chunk %s\n", str_chunk_type);
  220.  
  221.         switch (chunk_type)
  222.         {
  223.         case PNG_CN_IHDR:
  224.             p->width = convert_from_network_order(chunk_data);
  225.             p->height = convert_from_network_order(chunk_data+4);
  226.             p->bit_depth = *(chunk_data+8);
  227.             p->color_type = *(chunk_data+9);
  228.             p->compression_method = *(chunk_data+10);
  229.             p->filter_method = *(chunk_data+11);
  230.             p->interlace_method = *(chunk_data+12);
  231.             free (chunk_data);
  232.  
  233.             logerror("PNG IHDR information:\n");
  234.             logerror("Width: %i, Height: %i\n", p->width, p->height);
  235.             logerror("Bit depth %i, color type: %i\n", p->bit_depth, p->color_type);
  236.             logerror("Compression method: %i, filter: %i, interlace: %i\n",
  237.                     p->compression_method, p->filter_method, p->interlace_method);
  238.             break;
  239.  
  240.         case PNG_CN_PLTE:
  241.             p->num_palette=chunk_length/3;
  242.             p->palette=chunk_data;
  243.             logerror("%i palette entries\n", p->num_palette);
  244.             break;
  245.  
  246.         case PNG_CN_tRNS:
  247.             p->num_trans=chunk_length;
  248.             p->trans=chunk_data;
  249.             logerror("%i transparent palette entries\n", p->num_trans);
  250.             break;
  251.  
  252.         case PNG_CN_IDAT:
  253.             pidat->data = chunk_data;
  254.             pidat->length = chunk_length;
  255.             if ((pidat->next = malloc (sizeof(struct idat))) == 0)
  256.                 return 0;
  257.             pidat = pidat->next;
  258.             pidat->next = 0;
  259.             p->zlength += chunk_length;
  260.             break;
  261.  
  262.         case PNG_CN_tEXt:
  263.             {
  264.                 UINT8 *text=chunk_data;
  265.  
  266.                 while(*text++);
  267.                 chunk_data[chunk_length]=0;
  268.                 logerror("Keyword: %s\n", chunk_data);
  269.                 logerror("Text: %s\n", text);
  270.             }
  271.             free(chunk_data);
  272.             break;
  273.  
  274.         case PNG_CN_tIME:
  275.             {
  276.                 UINT8 *t=chunk_data;
  277.                 logerror("Image last-modification time: %i/%i/%i (%i:%i:%i) GMT\n",
  278.                     ((short)(*t) << 8)+ (short)(*(t+1)), *(t+2), *(t+3), *(t+4), *(t+5), *(t+6));
  279.             }
  280.  
  281.             free(chunk_data);
  282.             break;
  283.  
  284.         case PNG_CN_gAMA:
  285.             p->source_gamma     = convert_from_network_order(chunk_data)/100000.0;
  286.             logerror( "Source gamma: %f\n",p->source_gamma);
  287.  
  288.             free(chunk_data);
  289.             break;
  290.  
  291.         case PNG_CN_pHYs:
  292.             p->xres = convert_from_network_order(chunk_data);
  293.             p->yres = convert_from_network_order(chunk_data+4);
  294.             p->resolution_unit = *(chunk_data+8);
  295.             logerror("Pixel per unit, X axis: %i\n",p->xres);
  296.             logerror("Pixel per unit, Y axis: %i\n",p->yres);
  297.             if (p->resolution_unit)
  298.                 logerror("Unit is meter\n");
  299.             else
  300.                 logerror("Unit is unknown\n");
  301.             free(chunk_data);
  302.             break;
  303.  
  304.         case PNG_CN_IEND:
  305.             break;
  306.  
  307.         default:
  308.             if (chunk_type & 0x20000000)
  309.                 logerror("Ignoring ancillary chunk %s\n",str_chunk_type);
  310.             else
  311.                 logerror("Ignoring critical chunk %s!\n",str_chunk_type);
  312.             if (chunk_data)
  313.                 free(chunk_data);
  314.             break;
  315.         }
  316.     }
  317.     if ((p->zimage = (UINT8 *)malloc(p->zlength))==NULL)
  318.     {
  319.         logerror("Out of memory\n");
  320.         return 0;
  321.     }
  322.  
  323.     /* combine idat chunks to compressed image data */
  324.     temp = p->zimage;
  325.     while (ihead->next)
  326.     {
  327.         pidat = ihead;
  328.         memcpy (temp, pidat->data, pidat->length);
  329.         free (pidat->data);
  330.         temp += pidat->length;
  331.         ihead = pidat->next;
  332.         free (pidat);
  333.     }
  334.     p->bpp = (samples[p->color_type] * p->bit_depth) / 8;
  335.     p->rowbytes = ceil((p->width * p->bit_depth * samples[p->color_type]) / 8.0);
  336.  
  337.     if (png_inflate_image(p)==0)
  338.         return 0;
  339.  
  340.     if(png_unfilter (p)==0)
  341.         return 0;
  342.  
  343.     return 1;
  344. }
  345.  
  346. /*    Expands a p->image from p->bit_depth to 8 bit */
  347. int png_expand_buffer_8bit (struct png_info *p)
  348. {
  349.     int i,j, k;
  350.     UINT8 *inp, *outp, *outbuf;
  351.  
  352.     if (p->bit_depth < 8)
  353.     {
  354.         if ((outbuf = (UINT8 *)malloc(p->width*p->height))==NULL)
  355.         {
  356.             logerror("Out of memory\n");
  357.             return 0;
  358.         }
  359.  
  360.         inp = p->image;
  361.         outp = outbuf;
  362.  
  363.         for (i = 0; i < p->height; i++)
  364.         {
  365.             for(j = 0; j < p->width / ( 8 / p->bit_depth); j++)
  366.             {
  367.                 for (k = 8 / p->bit_depth-1; k >= 0; k--)
  368.                     *outp++ = (*inp >> k * p->bit_depth) & (0xff >> (8 - p->bit_depth));
  369.                 inp++;
  370.             }
  371.             if (p->width % (8 / p->bit_depth))
  372.             {
  373.                 for (k = p->width % (8 / p->bit_depth)-1; k >= 0; k--)
  374.                     *outp++ = (*inp >> k * p->bit_depth) & (0xff >> (8 - p->bit_depth));
  375.                 inp++;
  376.             }
  377.         }
  378.         free (p->image);
  379.         p->image = outbuf;
  380.     }
  381.     return 1;
  382. }
  383.  
  384. void png_delete_unused_colors (struct png_info *p)
  385. {
  386.     int i, tab[256], pen=0, trns=0;
  387.     UINT8 ptemp[3*256], ttemp[256];
  388.  
  389.     memset (tab, 0, 256*sizeof(int));
  390.     memcpy (ptemp, p->palette, 3*p->num_palette);
  391.     memcpy (ttemp, p->trans, p->num_trans);
  392.  
  393.     /* check which colors are actually used */
  394.     for (i = 0; i < p->height*p->width; i++)
  395.         tab[p->image[i]]++;
  396.  
  397.     /* shrink palette and transparency */
  398.     for (i = 0; i < p->num_palette; i++)
  399.         if (tab[i])
  400.         {
  401.             p->palette[3*pen+0]=ptemp[3*i+0];
  402.             p->palette[3*pen+1]=ptemp[3*i+1];
  403.             p->palette[3*pen+2]=ptemp[3*i+2];
  404.             if (i < p->num_trans)
  405.             {
  406.                 p->trans[pen] = ttemp[i];
  407.                 trns++;
  408.             }
  409.             tab[i] = pen++;
  410.         }
  411.  
  412.     /* remap colors */
  413.     for (i = 0; i < p->height*p->width; i++)
  414.         p->image[i]=tab[p->image[i]];
  415.  
  416.     if (p->num_palette!=pen)
  417.         logerror("%i unused pen(s) deleted\n", p->num_palette-pen);
  418.  
  419.     p->num_palette = pen;
  420.     p->num_trans = trns;
  421. }
  422.  
  423. /********************************************************************************
  424.  
  425.   PNG write functions
  426.  
  427. ********************************************************************************/
  428.  
  429. static void convert_to_network_order (UINT32 i, UINT8 *v)
  430. {
  431.     v[0]=i>>24;
  432.     v[1]=(i>>16)&0xff;
  433.     v[2]=(i>>8)&0xff;
  434.     v[3]=i&0xff;
  435. }
  436.  
  437. static int png_write_chunk(void *fp, UINT32 chunk_type, UINT8 *chunk_data, UINT32 chunk_length)
  438. {
  439.     UINT32 crc;
  440.     UINT8 v[4];
  441.     int written;
  442.  
  443.     /* write length */
  444.     convert_to_network_order(chunk_length, v);
  445.     written = osd_fwrite(fp, v, 4);
  446.  
  447.     /* write type */
  448.     convert_to_network_order(chunk_type, v);
  449.     written += osd_fwrite(fp, v, 4);
  450.  
  451.     /* calculate crc */
  452.     crc=crc32(0, v, 4);
  453.     if (chunk_length > 0)
  454.     {
  455.         /* write data */
  456.         written += osd_fwrite(fp, chunk_data, chunk_length);
  457.         crc=crc32(crc, chunk_data, chunk_length);
  458.     }
  459.     convert_to_network_order(crc, v);
  460.  
  461.     /* write crc */
  462.     written += osd_fwrite(fp, v, 4);
  463.  
  464.     if (written != 3*4+chunk_length)
  465.     {
  466.         logerror("Chunk write failed\n");
  467.         return 0;
  468.     }
  469.     return 1;
  470. }
  471.  
  472. static int png_write_file(void *fp, struct png_info *p)
  473. {
  474.     UINT8 ihdr[13];
  475.     char text[256];
  476.  
  477.     /* PNG Signature */
  478.     if (osd_fwrite(fp, PNG_Signature, 8) != 8)
  479.     {
  480.         logerror("PNG sig write failed\n");
  481.         return 0;
  482.     }
  483.  
  484.     /* PNG_CN_IHDR */
  485.     convert_to_network_order(p->width, ihdr);
  486.     convert_to_network_order(p->height, ihdr+4);
  487.     *(ihdr+8) = p->bit_depth;
  488.     *(ihdr+9) = p->color_type;
  489.     *(ihdr+10) = p->compression_method;
  490.     *(ihdr+11) = p->filter_method;
  491.     *(ihdr+12) = p->interlace_method;
  492.     logerror("Type(%d) Color Depth(%d)\n", p->color_type,p->bit_depth);
  493.     if (png_write_chunk(fp, PNG_CN_IHDR, ihdr, 13)==0)
  494.         return 0;
  495.  
  496.     /* PNG_CN_PLTE */
  497.     if (p->num_palette > 0)
  498.         if (png_write_chunk(fp, PNG_CN_PLTE, p->palette, p->num_palette*3)==0)
  499.             return 0;
  500.  
  501.     /* PNG_CN_IDAT */
  502.     if (png_write_chunk(fp, PNG_CN_IDAT, p->zimage, p->zlength)==0)
  503.         return 0;
  504.  
  505.     /* PNG_CN_tEXt */
  506.     sprintf (text, "Software");
  507. #ifdef MESS
  508.     sprintf (text+9, "MESS %s", build_version);
  509. #else
  510.     sprintf (text+9, "MAME %s", build_version);
  511. #endif
  512.     if (png_write_chunk(fp, PNG_CN_tEXt, (UINT8 *)text, 14+strlen(build_version))==0)
  513.         return 0;
  514.  
  515.     /* PNG_CN_IEND */
  516.     if (png_write_chunk(fp, PNG_CN_IEND, NULL, 0)==0)
  517.         return 0;
  518.  
  519.     return 1;
  520. }
  521.  
  522. static int png_filter(struct png_info *p)
  523. {
  524.     int i;
  525.     UINT8 *src, *dst;
  526.  
  527.     if((p->fimage = (UINT8 *)malloc (p->height*(p->rowbytes+1)))==NULL)
  528.     {
  529.         logerror("Out of memory\n");
  530.         return 0;
  531.     }
  532.  
  533.     dst = p->fimage;
  534.     src = p->image;
  535.  
  536.     for (i=0; i<p->height; i++)
  537.     {
  538.         *dst++ = 0; /* No filter */
  539.         memcpy (dst, src, p->rowbytes);
  540.         src += p->rowbytes;
  541.         dst += p->rowbytes;
  542.     }
  543.     return 1;
  544. }
  545.  
  546. static int png_deflate_image(struct png_info *p)
  547. {
  548.     unsigned long zbuff_size;
  549.  
  550.     zbuff_size = (p->height*(p->rowbytes+1))*1.1+12;
  551.  
  552.     if((p->zimage = (UINT8 *)malloc (zbuff_size))==NULL)
  553.     {
  554.         logerror("Out of memory\n");
  555.         return 0;
  556.     }
  557.  
  558.     if (compress(p->zimage, &zbuff_size, p->fimage, p->height*(p->rowbytes+1)) != Z_OK)
  559.     {
  560.         logerror("Error while deflating image\n");
  561.         return 0;
  562.     }
  563.     p->zlength = zbuff_size;
  564.  
  565.     return 1;
  566. }
  567.  
  568. static int png_pack_buffer (struct png_info *p)
  569. {
  570.     UINT8 *outp, *inp;
  571.     int i,j,k;
  572.  
  573.     outp = inp = p->image;
  574.  
  575.     if (p->bit_depth < 8)
  576.     {
  577.         for (i=0; i<p->height; i++)
  578.         {
  579.             for(j=0; j<p->width/(8/p->bit_depth); j++)
  580.             {
  581.                 for (k=8/p->bit_depth-1; k>=0; k--)
  582.                     *outp |= *inp++ << k * p->bit_depth;
  583.                 outp++;
  584.                 *outp = 0;
  585.             }
  586.             if (p->width % (8/p->bit_depth))
  587.             {
  588.                 for (k=p->width%(8/p->bit_depth)-1; k>=0; k--)
  589.                     *outp |= *inp++ << k * p->bit_depth;
  590.                 outp++;
  591.                 *outp = 0;
  592.             }
  593.         }
  594.     }
  595.     return 1;
  596. }
  597.  
  598.  
  599. /*********************************************************************
  600.  
  601.   Writes an osd_bitmap in a PNG file. If the depth of the bitmap
  602.   is 8, a color type 3 PNG with palette is written. Otherwise a
  603.   color type 2 true color RGB PNG is written.
  604.  
  605.  *********************************************************************/
  606. int png_write_bitmap(void *fp, struct osd_bitmap *bitmap)
  607. {
  608.     int i, j, c;
  609.     UINT8 *ip;
  610.     struct png_info p;
  611.  
  612.     memset (&p, 0, sizeof (struct png_info));
  613.     p.xscale = p.yscale = p.source_gamma = 0.0;
  614.     p.palette = p.trans = p.image = p.zimage = p.fimage = NULL;
  615.     p.width = bitmap->width;
  616.     p.height = bitmap->height;
  617.     p.color_type = (bitmap->depth == 8 ? 3: 2);
  618.  
  619.     if (p.color_type == 3)
  620.     {
  621.         if((p.palette = (UINT8 *)malloc (3*256))==NULL)
  622.         {
  623.             logerror("Out of memory\n");
  624.             return 0;
  625.         }
  626.         memset (p.palette, 0, 3*256);
  627.         /* get palette */
  628.         for (i = 0; i < Machine->drv->total_colors; i++)
  629.         {
  630.             c = Machine->pens[i];
  631.             osd_get_pen(c,&p.palette[3*c],&p.palette[3*c+1],&p.palette[3*c+2]);
  632.         }
  633.  
  634.         p.num_palette = 256;
  635.         if((p.image = (UINT8 *)malloc (p.height*p.width))==NULL)
  636.         {
  637.             logerror("Out of memory\n");
  638.             return 0;
  639.         }
  640.  
  641.         for (i = 0; i < p.height; i++)
  642.             memcpy(&p.image[i * p.width], bitmap->line[i], p.width);
  643.  
  644.         png_delete_unused_colors (&p);
  645.         p.bit_depth = p.num_palette > 16 ? 8 : p.num_palette > 4 ? 4 : p.num_palette > 2 ? 2 : 1;
  646.         p.rowbytes=ceil((p.width*p.bit_depth)/8.0);
  647.         if (png_pack_buffer (&p) == 0)
  648.             return 0;
  649.  
  650.     }
  651.     else
  652.     {
  653.         p.rowbytes = p.width * 3;
  654.         p.bit_depth = 8;
  655.         if((p.image = (UINT8 *)malloc (p.height * p.rowbytes))==NULL)
  656.         {
  657.             logerror("Out of memory\n");
  658.             return 0;
  659.         }
  660.  
  661.         ip = p.image;
  662.  
  663.         for (i = 0; i < p.height; i++)
  664.             for (j = 0; j < p.width; j++)
  665.             {
  666.                 osd_get_pen(((UINT16 *)bitmap->line[i])[j],ip, ip+1, ip+2);
  667.                 ip += 3;
  668.             }
  669.     }
  670.  
  671.     if(png_filter (&p)==0)
  672.         return 0;
  673.  
  674.     if (png_deflate_image(&p)==0)
  675.         return 0;
  676.  
  677.     if (png_write_file(fp, &p)==0)
  678.         return 0;
  679.  
  680.     if (p.palette) free (p.palette);
  681.     if (p.image) free (p.image);
  682.     if (p.zimage) free (p.zimage);
  683.     if (p.fimage) free (p.fimage);
  684.     return 1;
  685. }
  686.  
  687. /* End of source */
  688.  
  689.